package ai;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import model.Connected4Game;
import model.Slot;
import model.State;

public class MiniMaxPlayer extends AIPlayer {
	public MiniMaxPlayer(Color color, String description, int maxDepth,
			boolean enDepthCheck, BestChoiceType choiceType,
			boolean enReordering, List<HeuristicType> heuristics) {
		super(color, description, maxDepth, enDepthCheck, choiceType, enReordering, heuristics);
	}

	@Override
	public int executeMove() {
		return miniMaxDecision(board);
	}

	private int miniMaxDecision(Slot[][] boardState) {
		ArrayList<Integer> actions = getActions(boardState);
		ArrayList<NodeValue> returnValues = new ArrayList<NodeValue>();
		for (int i = 0; i < actions.size(); i++) {
			returnValues.add(getMinValue(
					result(boardState, actions.get(i), getPlayerSlot()), 1));
		}

		/**************************************************************
		 * BEST CHOICHE TYPE *
		 **************************************************************/
		int move;
		switch (choiceType) {
		case CENTERED:
			move = getCenteredShortestBestActionFromValues(actions,
					returnValues);
			break;
		case RANDOM:
			move = getRandomBestActionFromValues(actions, returnValues);
			break;
		default:
			move = getCenteredShortestBestActionFromValues(actions,
					returnValues);
			break;
		}

		return move;
	}

	private NodeValue getMaxValue(Slot[][] boardState, int actualDepth) {

		State s = Connected4Game.checkGameState(boardState);
		if (!s.equals(State.PLAYING) || actualDepth == maxDepth) {
			float utility = getUtility(s, boardState,getPlayerSlot());
			return new NodeValue(utility, actualDepth);
		}

		NodeValue v = new NodeValue(Float.NEGATIVE_INFINITY, actualDepth);
		for (int col : getActions(boardState)) {
			NodeValue returnValue = getMinValue(
					result(boardState, col, getPlayerSlot()), actualDepth + 1);
			v = returnValue.getValue() > v.getValue() ? returnValue : v;
		}

		return v;
	}

	private NodeValue getMinValue(Slot[][] boardState, int actualDepth) {

		State s = Connected4Game.checkGameState(boardState);
		if (!s.equals(State.PLAYING) || actualDepth == maxDepth) {
			float utility = getUtility(s, boardState,getOpponentSlot());
			return new NodeValue(utility, actualDepth);
		}

		NodeValue v = new NodeValue(Float.POSITIVE_INFINITY, actualDepth);
		for (int col : getActions(boardState)) {
			NodeValue returnValue = getMaxValue(
					result(boardState, col, getOpponentSlot()), actualDepth + 1);
			v = returnValue.getValue() < v.getValue() ? returnValue : v;
		}

		return v;
	}

}
